package rpc.remote.transport.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import rpc.factory.SingletonFactory;
import rpc.config.CustomShutDownHook;
import rpc.config.RpcServiceConfig;
import rpc.provider.ServiceProvider;
import rpc.provider.impl.ZkServiceProviderImpl;
import rpc.remote.transport.netty.code.RpcMessageDecoder;
import rpc.remote.transport.netty.code.RpcMessageEcnoder;
import rpc.utils.RuntimeUtil;
import rpc.utils.concurrent.threadpool.ThreadPoolFactoryUtils;

import java.net.InetAddress;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: NettyRpcServer
 * @Description: 基于Netty实现的服务端，用于接收客户端消息，根据客户端消息进行相应方法的调用并返回对应的结果
 * @author: Kowk
 * @since:1.0.0
 */
@Slf4j
@Component
public class NettyRpcServer {

    public static final int PORT = 8888;
    private final ServiceProvider serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);

    public void registerService(RpcServiceConfig rpcServiceConfig){
        serviceProvider.publishService(rpcServiceConfig);
    }

    //使用此注解后不需要担心Exception的处理,其可以为我们的代码生成一个try...catch块，并把异常向上抛出来
    @SneakyThrows
    public void start(){
        CustomShutDownHook.getCustomShutDownHook().clearAll();
        String host = InetAddress.getLocalHost().getHostAddress();
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
                RuntimeUtil.cpus() * 2,
                ThreadPoolFactoryUtils.createThreadFactory("service-handler-group",false)
        );
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    // TCP默认开启了 Nagle 算法，该算法的作用是尽可能的发送大数据快，减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                    .childOption(ChannelOption.TCP_NODELAY,true)
                    // 是否开启 TCP 底层心跳机制
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
                    .option(ChannelOption.SO_BACKLOG,128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 当客户端第一次进行请求的时候才会进行初始化
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 30 秒之内没有收到客户端请求的话就关闭连接
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new IdleStateHandler(30,0, 0, TimeUnit.SECONDS));
                            pipeline.addLast(new RpcMessageEcnoder());
                            pipeline.addLast(new RpcMessageDecoder());
                            pipeline.addLast(serviceHandlerGroup,new NettyRpcServerHandler());
                        }
                    });
            //绑定端口，同步等待绑定成功
            ChannelFuture future = bootstrap.bind(host,PORT).sync();
            //等待服务监听端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("开启服务时发生异常",e);
        } finally {
            log.error("关闭bossGroup和workGroup");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            serviceHandlerGroup.shutdownGracefully();
        }
    }
}
